#include <functional>
#include <signal.h>
#include <sys/wait.h>

#include "global.h"
#include "logger.h"
#include "funcutil.h"


using SigHandFunc = void (*)(int, siginfo_t*, void*);

class SigAct
{
public:
    SigAct(int signo, const std::string&& sig_name, SigHandFunc sfun)
    : m_signo(signo),
      m_signame(std::move(sig_name)),
      m_sig_fun(sfun)
    {
    }
    void operator()(int signo, siginfo_t* siginfo, void* context)
    {
        m_sig_fun(signo, siginfo, context);
    }
    bool operator==(int signo) const
    {
        return m_signo == signo;
    }

public:
    int m_signo;
    const std::string m_signame;
    SigHandFunc m_sig_fun;
};


static void sig_handler(int signo, siginfo_t* siginfo, void* context); //信号处理函数
static void get_child_status(); //获得子进程返回值

static const std::vector<SigAct> sigactions = {
    {SIGINT, "SIGINT", sig_handler},
    {SIGHUP, "SIGHUP", sig_handler},
    {SIGCHLD, "SIGCHLD", sig_handler},
    {SIGTERM, "SIGTERM", sig_handler},
    {SIGQUIT, "SIGQUIT", sig_handler},
    {SIGSYS, "SIGSYS, SIGIGN", sig_handler},
    {0, "", nullptr}
};

int init_signals()
{
    struct sigaction sa;
    for (const auto& siga : sigactions)
    {
        memset(&sa, 0, sizeof(struct sigaction));
        if (siga == 0) break;
        if (siga.m_sig_fun)
        {
            sa.sa_sigaction = siga.m_sig_fun; //如果数组中注册了信号处理函数，则将函数指针赋给结构体
            sa.sa_flags = SA_SIGINFO; //打上标记，告知系统遇到这个信号时需要调用处理函数
        }
        else
        {
            sa.sa_handler = SIG_IGN; //若未设置处理函数，则告知系统忽略该信号，以免误杀进程
        }

        sigemptyset(&sa.sa_mask); //清空sa结构体中的信号屏蔽，保证一定能收到信号
        if (sigaction(siga.m_signo, &sa, NULL) == -1)
        {
            wb::Logger::logger_core(wb::LogLevel::Error, errno, "sigaction(%s) failed", siga.m_signame.c_str());
            return -1;
        }

    } //end for

    return 0;
}

static void sig_handler(int signo, siginfo_t* siginfo, void* context)
{
    for (const auto& siga : sigactions)
    {
        if (siga == signo)
        {
            break; //找到注册的信号
        }
    }

    switch (signo)
    {
        case SIGCHLD:
            reap = 1;
            get_child_status();
            break;
    }
}

static void get_child_status()
{
    pid_t cpid;
    int status;
    int savedErrno;

    for (;;)
    {
        cpid = waitpid(-1, &status, WNOHANG); //等待任意一个子进程的返回值

        if (cpid == 0)
        {
            return ;
        }

        if (cpid == -1)
        {
            savedErrno = errno;
            switch (savedErrno)
            {
                case EINTR: //被信号中断
                    continue;
                case ECHILD: //没有子进程
                    return;
            }
        }

        if (WTERMSIG(status)) //非0则代表返回错误
        {
            wb::Logger::logger_core(wb::LogLevel::Warn, savedErrno, "pid = %P exit on exitcode %d", cpid, WTERMSIG(status));
        }
        else  //返回值为0
        {
            wb::Logger::logger_core(wb::LogLevel::Info, savedErrno, "pid = %P exit normally(%d)", cpid, WEXITSTATUS(status));
        }
    }
}